﻿namespace ImageProcessing
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading;
    using ImageProcessing;

    /// <summary>
    /// Class of Operations on Images
    /// </summary>
    public class Operations
    {
        /// <summary>
        /// Crops the specified orginal image.
        /// </summary>
        /// <param name="orginalImage">The orginal image.</param>
        /// <param name="topLeft">Up left point .</param>
        /// <param name="downRight">Down right point .</param>
        /// <returns>2D array of ImageStructure represent the cropped image</returns>
        public static ImageStructure[,] Crop(ImageStructure[,] orginalImage, Point topLeft, Point downRight)
        {
            int newWidth, newHeight;
            newWidth = downRight.X - topLeft.X;
            newHeight = downRight.Y - topLeft.Y;

            if (newWidth > 0 && newHeight > 0)
            {
                ImageStructure[,] croppedImage = new ImageStructure[newHeight, newWidth];
                int rowsPerThread = newHeight / Environment.ProcessorCount;
                Semaphore s = new Semaphore(1, 1000);
                int minHeight = 0, maxHeight = 0;
                List<Thread> threadList = new List<Thread>();
                for (int i = 1; i < Environment.ProcessorCount; i++)
                {
                    s.WaitOne();
                    minHeight = (i * rowsPerThread) + topLeft.Y;
                    maxHeight = ((i + 1) * rowsPerThread) + topLeft.Y;
                    if (i == (Environment.ProcessorCount - 1))
                    {
                        Thread thread = new Thread(delegate() { new Operations().CropThreading(s, minHeight, downRight.Y, orginalImage, croppedImage, topLeft, downRight); });
                        thread.Start();
                        threadList.Add(thread);
                        break;
                    }

                    Thread t = new Thread(delegate() { new Operations().CropThreading(s, minHeight, maxHeight, orginalImage, croppedImage, topLeft, downRight); });
                    t.Start();
                    threadList.Add(t);
                }

                new Operations().CropThreading(s, topLeft.Y, rowsPerThread + topLeft.Y, orginalImage, croppedImage, topLeft, downRight);
                foreach (Thread t in threadList)
                {
                    t.Join();
                }
                Main.ImageList[Main.CurrImage].UpdateImage(croppedImage);
                Main.ImageList[Main.CurrImage].BackupImageArray();
                return croppedImage;
            }
            else
            {
                return orginalImage;
            }
        }

        /// <summary>
        /// Crops the specified orginal image.
        /// </summary>
        /// <param name="orginalImage">The orginal image.</param>
        /// <param name="topLeft">Up left point .</param>
        /// <param name="downRight">Down right point .</param>
        /// <returns>2D array of ImageStructure represent the cropped image</returns>
        public static ImageStructure[,] CropWithoutUpdate(ImageStructure[,] orginalImage, Point topLeft, Point downRight)
        {
            int newWidth, newHeight;
            newWidth = downRight.X - topLeft.X;
            newHeight = downRight.Y - topLeft.Y;

            if (newWidth > 0 && newHeight > 0)
            {
                ImageStructure[,] croppedImage = new ImageStructure[newHeight, newWidth];

                int rowsPerThread = newHeight / Environment.ProcessorCount;
                Semaphore s = new Semaphore(1, 1000);
                int minHeight = 0, maxHeight = 0;
                List<Thread> threadList = new List<Thread>();
                for (int i = 1; i < Environment.ProcessorCount; i++)
                {
                    s.WaitOne();
                    minHeight = (i * rowsPerThread) + topLeft.Y;
                    maxHeight = ((i + 1) * rowsPerThread) + topLeft.Y;
                    if (i == (Environment.ProcessorCount - 1))
                    {
                        Thread thread = new Thread(delegate() { new Operations().CropThreading(s, minHeight, downRight.Y, orginalImage, croppedImage, topLeft, downRight); });
                        thread.Start();
                        threadList.Add(thread);
                        break;
                    }

                    Thread t = new Thread(delegate() { new Operations().CropThreading(s, minHeight, maxHeight, orginalImage, croppedImage, topLeft, downRight); });
                    t.Start();
                    threadList.Add(t);
                }

                new Operations().CropThreading(s, topLeft.Y, rowsPerThread + topLeft.Y, orginalImage, croppedImage, topLeft, downRight);
                foreach (Thread t in threadList)
                {
                    t.Join();
                }
                return croppedImage;
            }
            else
            {
                return orginalImage;
            }
        }

        /// <summary>
        /// Subtructs the specified Images (first image - second image) .
        /// </summary>
        /// <param name="firstImage">The first image.</param>
        /// <param name="secondImage">The second image.</param>
        /// <returns>2D array of ImageStructure represent the subtructed image</returns>
        public static ImageStructure[,] Subtract(ImageStructure[,] firstImage, ImageStructure[,] secondImage)
        {
            int height = firstImage.GetLength(0);
            int width = firstImage.GetLength(1);
            secondImage = new ImageStructure[height, width];
            secondImage = ResizeImage.BicubicResize(width, height, secondImage, 3);
            ImageStructure[,] result = new ImageStructure[height, width];
            int rowsPerThread = height / Environment.ProcessorCount;
            Semaphore s = new Semaphore(1, 1000);
            int minHeight = 0, maxHeight = 0;
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i++)
            {
                s.WaitOne();
                minHeight = i * rowsPerThread;
                maxHeight = (i + 1) * rowsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    Thread thread = new Thread(delegate() { new Operations().SubtractThreading(s, minHeight, height, width, result, firstImage, secondImage); });
                    thread.Start();
                    threadList.Add(thread);
                    break;
                }

                Thread t = new Thread(delegate() { new Operations().SubtractThreading(s, minHeight, maxHeight, width, result, firstImage, secondImage); });
                t.Start();
                threadList.Add(t);
            }

            foreach (Thread t in threadList)
            {
                t.Join();
            }

            return result;
        }

        /// <summary>
        /// Displays the matrix.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>
        /// Bitmap of the image Matrix
        /// </returns>
        public static Bitmap DisplayMatrix(ImageStructure[,] image)
        {
            int width = image.GetLength(1);
            int height = image.GetLength(0);
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            unsafe
            {
                BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int pointerAddress = (int)bmpD.Scan0;
                int extraBytes = bmpD.Stride - (width * 3);
                int columnsPerThread = height / Environment.ProcessorCount;
                Thread t = null;
                int minHeight = 0, maxHeight = 0;
                Semaphore s = new Semaphore(1, 1000);
                List<Thread> threadList = new List<Thread>();
                for (int i = 1; i < Environment.ProcessorCount; i += 0)
                {
                    s.WaitOne();
                    minHeight = i * columnsPerThread;
                    maxHeight = (i + 1) * columnsPerThread;
                    if (i == (Environment.ProcessorCount - 1))
                    {
                        t = new Thread(delegate() { new Operations().DisplayMatrixThreadingLoop(s, minHeight, height, width, image, pointerAddress, extraBytes); });
                        t.Start();
                        threadList.Add(t);
                        break;
                    }

                    t = new Thread(delegate() { new Operations().DisplayMatrixThreadingLoop(s, minHeight, maxHeight, width, image, pointerAddress, extraBytes); });
                    t.Start();
                    threadList.Add(t);
                    i++;
                }

                new Operations().DisplayMatrixThreadingLoop(s, 0, columnsPerThread, width, image, pointerAddress, extraBytes);
                foreach (Thread thread in threadList)
                {
                    thread.Join();
                }

                bmp.UnlockBits(bmpD);
            }

            return bmp;
        }

        /// <summary>
        /// Displays the matrix.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>
        /// Bitmap of the image Matrix
        /// </returns>
        public static Bitmap DisplayMatrixAlpha(ImageStructure[,] image)
        {
            int width = image.GetLength(1);
            int height = image.GetLength(0);
            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format32bppArgb);
            unsafe
            {
                BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                int pointerAddress = (int)bmpD.Scan0;
                int extraBytes = bmpD.Stride - (width * 4);
                byte* p = (byte*)pointerAddress;
                for (int i = 0; i < height; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        p[0] = image[i, j].A;
                        p[1] = image[i, j].R;
                        p[2] = image[i, j].G;
                        p[3] = image[i, j].B;

                        p += 4;
                    }

                    p += extraBytes;
                }

                bmp.UnlockBits(bmpD);
            }

            return bmp;
        }

        /// <summary>
        /// Mirrors the specified image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <param name="reflectOnX">if set to true [reflect on X].</param>
        /// <returns>reflected image</returns>
        public static ImageStructure[,] Mirror(ImageStructure[,] image, bool reflectOnX)
        {
            ImageStructure[,] newImage = new ImageStructure[image.GetLength(0), image.GetLength(1)];
            int columnsPerThread = image.GetLength(0) / Environment.ProcessorCount;
            int minHeight = 0, maxHeight = 0;
            Thread t = null;
            Semaphore s = new Semaphore(1, 1000);
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i++)
            {
                s.WaitOne();
                minHeight = i * columnsPerThread;
                maxHeight = (i + 1) * columnsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    t = new Thread(delegate() { new Operations().MirrorThreading(s, minHeight, image.GetLength(0), newImage, image, reflectOnX); });
                    t.Start();
                    threadList.Add(t);
                    break;
                }

                t = new Thread(delegate() { new Operations().MirrorThreading(s, minHeight, maxHeight, newImage, image, reflectOnX); });
                t.Start();
                threadList.Add(t);
            }

            new Operations().MirrorThreading(s, 0, columnsPerThread, newImage, image, reflectOnX);
            foreach (Thread thread in threadList)
            {
                thread.Join();
            }

            return newImage;
        }

        /// <summary>
        /// Copies the image.
        /// </summary>
        /// <param name="imageStructArray">The image struct array.</param>
        /// <param name="width">The width.</param>
        /// <param name="height">The height.</param>
        /// <returns>Copied Image.</returns>
        public static ImageStructure[,] CopyImage(ImageStructure[,] imageStructArray, int width, int height)
        {
            ImageStructure[,] newStructArray = new ImageStructure[height, width];
            int columnsPerThread = height / Environment.ProcessorCount;
            int minHeight = 0, maxHeight = 0;
            Thread t = null;
            Semaphore s = new Semaphore(1, 1000);
            List<Thread> threadList = new List<Thread>();
            for (int i = 1; i < Environment.ProcessorCount; i += 0)
            {
                s.WaitOne();
                minHeight = i * columnsPerThread;
                maxHeight = (i + 1) * columnsPerThread;
                if (i == (Environment.ProcessorCount - 1))
                {
                    t = new Thread(delegate() { new Operations().CopyImageThreading(s, minHeight, height, imageStructArray, newStructArray, width); });
                    t.Start();
                    threadList.Add(t);
                    break;
                }

                t = new Thread(delegate() { new Operations().CopyImageThreading(s, minHeight, maxHeight, imageStructArray, newStructArray, width); });
                t.Start();
                threadList.Add(t);
                i++;
            }

            new Operations().CopyImageThreading(s, 0, columnsPerThread, imageStructArray, newStructArray, width);
            foreach (Thread thread in threadList)
            {
                thread.Join();
            }

            return newStructArray;
        }

        /// <summary>
        /// Greys the image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Greyed Image</returns>
        public static ImageStructure[,] GreyScale(ImageStructure[,] image)
        {
            int height = image.GetLength(0);
            int width = image.GetLength(1);

            ImageStructure[,] newImage = new ImageStructure[height, width];
            Bitmap b1, b2, br;
            b1 = OpenImage.ConvertStructureToBitmap(image, width, height);
            br = new Bitmap(width, height, PixelFormat.Format24bppRgb);

            unsafe
            {
                BitmapData bmd1, bmd3;
                bmd1 = b1.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                bmd3 = br.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int exrtraBytes = bmd1.Stride - width * 3;
                PHFastCpp.PHGreyscale((int)bmd1.Scan0, (int)bmd3.Scan0, width, height, exrtraBytes);

                br.UnlockBits(bmd3);
                b1.UnlockBits(bmd1);

                return Operations.ConvertBitmapToStructure(br);
            }
        }


        /// <summary>
        /// Greys the image.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Greyed Image</returns>
        public static Bitmap GreyScale(Bitmap image)
        {
            int height = image.Height;
            int width = image.Width;

            Bitmap b1, b2, br;
            b1 = image;
            br = new Bitmap(width, height, PixelFormat.Format24bppRgb);

            unsafe
            {
                BitmapData bmd1, bmd3;
                bmd1 = b1.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                bmd3 = br.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                int exrtraBytes = bmd1.Stride - width * 3;
                PHFastCpp.PHGreyscale((int)bmd1.Scan0, (int)bmd3.Scan0, width, height, exrtraBytes);

                br.UnlockBits(bmd3);
                b1.UnlockBits(bmd1);

                return br;
            }
        }

        /// <summary>
        /// Reomves the empty rows.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Image matrix with removed blank rows</returns>
        public static ImageStructure[,] RemoveEmptyRows(ImageStructure[,] image)
        {
            int deletedRows = 0;
            bool topEnd = false;
            ImageStructure[,] newImage;
            int height = image.GetLength(0);
            int width = image.GetLength(1);

            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    if (image[i, j].R != 0 || image[i, j].G != 0 || image[i, j].B != 0)
                    {
                        topEnd = true;
                        break;
                    }

                    if (j == width - 1)
                    {
                        deletedRows++;
                    }
                }

                if (topEnd)
                {
                    break;
                }
            }

            int newHeight = height - (deletedRows * 2);
            newImage = new ImageStructure[newHeight, width];
            for (int i = 0; i < newHeight; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newImage[i, j] = image[i + deletedRows, j];
                }
            }

            return newImage;
        }

        /// <summary>
        /// Reomves the empty colomns.
        /// </summary>
        /// <param name="image">The image.</param>
        /// <returns>Image matrix with removed columns</returns>
        public static ImageStructure[,] RemoveEmptyColomns(ImageStructure[,] image)
        {
            int deletedColomns = 0;
            bool leftEnd = false;
            ImageStructure[,] newImage;
            int height = image.GetLength(0);
            int width = image.GetLength(1);

            for (int j = 0; j < width; j++)
            {
                for (int i = 0; i < height; i++)
                {
                    if (image[i, j].R != 0 || image[i, j].G != 0 || image[i, j].B != 0)
                    {
                        leftEnd = true;
                        break;
                    }

                    if (i == height - 1)
                    {
                        deletedColomns++;
                    }
                }

                if (leftEnd)
                {
                    break;
                }
            }

            int newWidth = width - (deletedColomns * 2);
            newImage = new ImageStructure[height, newWidth];
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < newWidth; j++)
                {
                    newImage[i, j] = image[i, j + deletedColomns];
                }
            }

            return newImage;
        }

        /// <summary>
        /// Converts the bitmap to structure.
        /// </summary>
        /// <param name="bmp">The BMP.</param>
        /// <returns>Struct image</returns>
        public static ImageStructure[,] ConvertBitmapToStructure(Bitmap bmp)
        {
            ImageStructure[,] newImage = new ImageStructure[bmp.Height, bmp.Width];
            unsafe
            {
                BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat);
                byte* pointerAddress = (byte*)bmpD.Scan0;
                int extraBytes = 0;
                switch (bmpD.PixelFormat)
                {
                    case PixelFormat.Format16bppArgb1555:
                        extraBytes = bmpD.Stride - (bmp.Width * 2);
                        break;
                    case PixelFormat.Format16bppGrayScale:
                        extraBytes = bmpD.Stride - (bmp.Width * 2);
                        break;
                    case PixelFormat.Format16bppRgb555:
                        extraBytes = bmpD.Stride - (bmp.Width * 2);
                        break;
                    case PixelFormat.Format16bppRgb565:
                        extraBytes = bmpD.Stride - (bmp.Width * 2);
                        break;
                    case PixelFormat.Format24bppRgb:
                        extraBytes = bmpD.Stride - (bmp.Width * 3);
                        break;
                    case PixelFormat.Format32bppArgb:
                        extraBytes = bmpD.Stride - (bmp.Width * 4);
                        break;
                    case PixelFormat.Format32bppPArgb:
                        extraBytes = bmpD.Stride - (bmp.Width * 4);
                        break;
                    case PixelFormat.Format32bppRgb:
                        extraBytes = bmpD.Stride - (bmp.Width * 4);
                        break;
                    case PixelFormat.Format8bppIndexed:
                        extraBytes = bmpD.Stride - bmp.Width;
                        break;
                }

                for (int i = 0; i < bmp.Height; i++)
                {
                    for (int j = 0; j < bmp.Width; j++)
                    {
                        if (bmp.PixelFormat == PixelFormat.Format8bppIndexed)
                        {
                            newImage[i, j].R = newImage[i, j].G = newImage[i, j].B = pointerAddress[0];
                            pointerAddress++;
                        }

                        if (bmp.PixelFormat == PixelFormat.Format24bppRgb)
                        {
                            newImage[i, j].R = pointerAddress[0];
                            newImage[i, j].G = pointerAddress[1];
                            newImage[i, j].B = pointerAddress[2];
                            pointerAddress += 3;
                        }
                        else if (bmp.PixelFormat == PixelFormat.Format32bppArgb || bmp.PixelFormat == PixelFormat.Format32bppPArgb || bmp.PixelFormat == PixelFormat.Format32bppRgb)
                        {
                            newImage[i, j].R = pointerAddress[0];
                            newImage[i, j].G = pointerAddress[1];
                            newImage[i, j].B = pointerAddress[2];
                            newImage[i, j].A = pointerAddress[3];
                            pointerAddress += 4;
                        }
                    }

                    pointerAddress += extraBytes;
                }

                bmp.UnlockBits(bmpD);
            }

            return newImage;
        }

        /// <summary>
        /// Gets the color of the image of.
        /// </summary>
        /// <param name="color">The color.</param>
        /// <param name="height">The height.</param>
        /// <param name="width">The width.</param>
        /// <returns>Newimage with copied color</returns>
        public static ImageStructure[,] GetImageOfColor(Color color, int height, int width)
        {
            ImageStructure[,] newimage = new ImageStructure[height, width];
            for (int i = 0; i < height; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newimage[i, j].R = color.R;
                    newimage[i, j].G = color.G;
                    newimage[i, j].B = color.B;
                }
            }

            return newimage;
        }

        /// <summary>
        /// Copies a bitmap to a new one for threading purposes.
        /// </summary>
        /// <param name="original"></param>
        /// <returns>Copied bitmap</returns>
        public static Bitmap CopyBitmap(Bitmap original)
        {
            Bitmap bmp = new Bitmap(original.Width, original.Height, PixelFormat.Format24bppRgb);
            BitmapData bmpD = bmp.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
            BitmapData originalD = original.LockBits(new Rectangle(0, 0, original.Width, original.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

            int oldExtraBytes = originalD.Stride - originalD.Width * 3;
            int newExtraBytes = bmpD.Stride - bmpD.Width * 3;

            PHFastCpp.PHCopyBitmap((int)originalD.Scan0, (int)bmpD.Scan0, oldExtraBytes, newExtraBytes, bmp.Width, bmp.Height);

            bmp.UnlockBits(bmpD);
            original.UnlockBits(originalD);
            return bmp;
        }

        /// <summary>
        /// Subtracts the threading.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="width">The width.</param>
        /// <param name="result">The result.</param>
        /// <param name="firstImage">The first image.</param>
        /// <param name="secondImage">The second image.</param>
        private void SubtractThreading(Semaphore s, int minHeight, int maxHeight, int width, ImageStructure[,] result, ImageStructure[,] firstImage, ImageStructure[,] secondImage)
        {
            s.Release();
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    try
                    {
                        result[i, j] = firstImage[i, j] - secondImage[i, j];
                    }
                    catch
                    {
                        result[i, j] = firstImage[i, j];
                    }
                }
            }
        }

        /// <summary>
        /// Mirrors the threading.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="newImage">The new image.</param>
        /// <param name="image">The image.</param>
        /// <param name="reflectOnX">if set to true [reflect on X].</param>
        private void MirrorThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] newImage, ImageStructure[,] image, bool reflectOnX)
        {
            s.Release();
            int imageHeight = image.GetLength(0) - 1;
            int imageWidth = image.GetLength(1) - 1;
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = 0; j < image.GetLength(1); j++)
                {
                    if (reflectOnX)
                    {
                        newImage[imageHeight - i, j] = image[i, j];
                    }
                    else
                    {
                        newImage[i, imageWidth - j] = image[i, j];
                    }
                }
            }
        }

        /// <summary>
        /// Copies the image threading.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="imageStructArray">The image struct array.</param>
        /// <param name="newStructArray">The temp struct array.</param>
        /// <param name="width">The width.</param>
        private void CopyImageThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] imageStructArray, ImageStructure[,] newStructArray, int width)
        {
            s.Release();
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    newStructArray[i, j].R = imageStructArray[i, j].R;
                    newStructArray[i, j].G = imageStructArray[i, j].G;
                    newStructArray[i, j].B = imageStructArray[i, j].B;
                }
            }
        }

        /// <summary>
        /// Displays the matrix threading loop.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="width">The width.</param>
        /// <param name="image">The image.</param>
        /// <param name="pointerAddress">The pointer address.</param>
        /// <param name="extraBytes">The extra bytes.</param>
        private void DisplayMatrixThreadingLoop(Semaphore s, int minHeight, int maxHeight, int width, ImageStructure[,] image, int pointerAddress, int extraBytes)
        {
            unsafe
            {
                s.Release();
                int pointerIndex = (minHeight * width * 3) + (minHeight * extraBytes);
                byte* p = (byte*)pointerAddress;
                for (int i = minHeight; i < maxHeight; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        p[pointerIndex] = image[i, j].R;
                        p[pointerIndex + 1] = image[i, j].G;
                        p[pointerIndex + 2] = image[i, j].B;

                        pointerIndex += 3;
                    }

                    pointerIndex += extraBytes;
                }

                return;
            }
        }

        /// <summary>
        /// Displays the matrix with Alpha structure threading loop.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="width">The width.</param>
        /// <param name="image">The image.</param>
        /// <param name="pointerAddress">The pointer address.</param>
        /// <param name="extraBytes">The extra bytes.</param>
        private void DisplayMatrixAlphaThreadingLoop(Semaphore s, int minHeight, int maxHeight, int width, ImageStructure[,] image, int pointerAddress, int extraBytes)
        {
            unsafe
            {
                s.Release();
                int pointerIndex = (minHeight * width * 4) + (minHeight * extraBytes);
                byte* p = (byte*)pointerAddress;
                for (int i = minHeight; i < maxHeight; i++)
                {
                    for (int j = 0; j < width; j++)
                    {
                        p[pointerIndex] = image[i, j].A;
                        p[pointerIndex + 1] = image[i, j].R;
                        p[pointerIndex + 2] = image[i, j].G;
                        p[pointerIndex + 3] = image[i, j].B;

                        pointerIndex += 4;
                    }

                    pointerIndex += extraBytes;
                }

                return;
            }
        }

        /// <summary>
        /// Crops the threading.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="orginalImage">The orginal image.</param>
        /// <param name="croppedImage">The cropped image.</param>
        /// <param name="topLeft">Up left.</param>
        /// <param name="downRight">Down right.</param>
        private void CropThreading(Semaphore s, int minHeight, int maxHeight, ImageStructure[,] orginalImage, ImageStructure[,] croppedImage, Point topLeft, Point downRight)
        {
            s.Release();
            for (int j = minHeight; j < maxHeight; j++)
            {
                for (int i = topLeft.X; i < downRight.X; i++)
                {
                    croppedImage[j - topLeft.Y, i - topLeft.X] = orginalImage[j, i];
                }
            }
        }

        /// <summary>
        /// Greyscale threading function.
        /// </summary>
        /// <param name="s">The s.</param>
        /// <param name="minHeight">Height of the min.</param>
        /// <param name="maxHeight">Height of the max.</param>
        /// <param name="width">The width.</param>
        /// <param name="image">The image.</param>
        /// <param name="newImage">The new image.</param>
        private void GreyscaleThreading(Semaphore s, int minHeight, int maxHeight, int width, ImageStructure[,] image, ImageStructure[,] newImage)
        {
            s.Release();
            int greyValue = 0;
            for (int i = minHeight; i < maxHeight; i++)
            {
                for (int j = 0; j < width; j++)
                {
                    greyValue = (image[i, j].R + image[i, j].G + image[i, j].B) / 3;
                    newImage[i, j].R = newImage[i, j].G = newImage[i, j].B = (byte)greyValue;
                }
            }
        }
    }
}